热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

刷题笔记:探索乘积小于K的子数组问题

篇首语:本文由编程笔记#小编为大家整理,主要介绍了刷题日记乘积小于K的子数组相关的知识,希望对你有一定的参考价值。 乘积小于K的子数组 给定一个正整数数组 nums 和整数 k ,请找出

篇首语:本文由编程笔记#小编为大家整理,主要介绍了刷题日记乘积小于K的子数组相关的知识,希望对你有一定的参考价值。



乘积小于K的子数组

给定一个正整数数组 nums 和整数 k ,请找出该数组内乘积小于 k 的连续子数组个数

示例 1:

输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8 个乘积小于 100 的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。

示例 2:

输入: nums = [1,2,3], k = 0
输出: 0

提示:


  • 1 <&#61; nums.length <&#61; 3 * 10^4
  • 1 <&#61; nums[i] <&#61; 1000
  • 0 <&#61; k <&#61; 10^6

题目链接


思路一&#xff1a;前缀和 &#43; 二分

二分查找常用函数&#xff1a;

头文件 #include


  • upper_bound(begin, end, num) &#xff1a;返回在 [begin, end) 二分查找的**第一个大于 num** 的数的索引&#xff0c;不存在返回 end
  • lower_bound(begin, end, num) &#xff1a;返回在 [begin, end) 二分查找的**第一个大于等于 num** 的数的索引&#xff0c;不存在返回 end

算法思路&#xff1a;


  • 使用乘积作为二分的依据&#xff0c;会出现 整形溢出&#xff0c;防止溢出&#xff0c;等式两边取对数&#xff1a;


    • mul[i&#43;1] &#61; mul[i] * nums[i]; //乘积会整型溢出
      // 等式两边取对数
      log(mul[i&#43;1]) &#61; log(mul[i] * nums[i]) &#61; log(mul[i]) &#43; log(nums[i]);
      // 乘积变为对数和
      // 令 sum[i] &#61; log(mul[i])
      sum[i&#43;1] &#61; sum[i] &#43; log(nums[i]);

  • 题目所求为 乘积小于k的子数组&#xff0c;遍历每个数字 nums[i]&#xff0c;枚举区间左端点 i &#xff0c;找最小边界 bound&#xff0c;满足&#xff1a;


    • i <&#61; bound
    • nums[i]*num[i&#43;1]*...*nums[bound] >&#61; ksum[bound] - sum[i-1] >&#61; log(k)
    • 找 第一个bound&#xff0c;sum[bound] >&#61; log(k) &#43; sum[i-1] &#61;&#61;> 使用 lower_bound
  • 注意&#xff1a;因为涉及到对数&#xff0c;因此 sum[] 数组使用 double 类型



    double 类型只保证15位小数精确&#xff0c;为防止计算误差&#xff1a;


    1. 题目中 1 <&#61; nums[i] <&#61; 10001 <&#61; nums.length <&#61; 3 * 10^4&#xff0c;可得最大乘积为1000^3*10^4&#xff0c;取对数为 30000*ln(1000)&#xff0c;ln(1000)&#61;6.9 &#xff0c;整数数位最大为6位

    2. 防止计算误差&#xff08;即15位后省去的小数&#xff09;&#xff0c;小数最少为9位&#xff0c;加上 1e-10

      sum[bound] - sum[i-1] &#43; 1e-10 >&#61; log(k)


    条件&#xff1a;sum[bound] >&#61; log(k) &#43; sum[i -1] - 1e-10

  • 对每个区间左端点 i &#xff0c;找到的边界 bound&#xff0c;则以下区间均满足条件&#xff1a;



    [i, i], [i, i&#43;1], [i, i&#43;2],..., [i, bound-1]


    bound - 1 - i &#43; 1 &#61; bound - i 个区间


代码&#xff1a;

class Solution
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k)
int n &#61; nums.size();
// long long mul[n&#43;1];
double sum[n&#43;1];
for(int i &#61; 0;i < n;i &#43;&#43; )
// mul[i&#43;1] &#61; mul[i] * nums[i]; //乘积会整型溢出
// 等式两边取对数 log(mul[i&#43;1]) &#61; log(mul[i] * nums[i]) &#61; log(mul[i]) &#43; log(nums[i])
// 令 sum[i] &#61; log(mul[i])
sum[i&#43;1] &#61; sum[i] &#43; log(nums[i]);

int cnt &#61; 0;
for(int i &#61; 1;i <&#61; n;i &#43;&#43; )
int l &#61; lower_bound(sum &#43; i, sum &#43; n &#43; 1, sum[i-1] &#43; log(k) - 1e-10) - sum;
cnt &#43;&#61; (l - i);

return cnt;

;
// 遍历每个i 二分找第一个乘积大于等于k的边界bound 则i...bound-1都满足条件

时间复杂度&#xff1a;O(nlogn)

空间复杂度&#xff1a;O(n)


思路二&#xff1a;滑动窗口

双指针 left, right


算法思路&#xff1a;


  • 窗口右边界逐个递增&#xff08;迭代&#xff09;
  • [left, right] 内乘积 prod >&#61; k 时&#xff0c;窗口左边界右移&#xff0c;减小窗口&#xff0c;直到 prod 为止
  • 过程中不断叠加统计 [left, right] 内的新增的子区间数量

【统计子区间】

例&#xff1a;10 5 2 6 k &#61; 101
10 5 2 6
区间1&#xff1a;l,r [10] &#43;1
区间2&#xff1a;l r [10][5][10,5] &#43;1&#43;2
区间3&#xff1a;l r [10][5][10,5][2][5,2][10,5,2] &#43;1&#43;2&#43;3
区间4&#xff1a;l r 10*5*2*6&#61;600 > 101
区间5&#xff1a; l r [6][2,6][5,2,6] &#43;1&#43;2&#43;3&#43;3
l r 结束

注&#xff1a;
1.对每个区间[l,r]只统计包括nums[r]的子区间 &#61;&#61;&#61;> 防止重复
2.对每个满足乘积prod < k的区间统计子区间个数
3.[规律]每个区间增加的子区间个数为 <窗口长度>

代码&#xff1a;

class Solution
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k)
if(k &#61;&#61; 0) return 0;

int n &#61; nums.size();
int l &#61; 0, r &#61; 0;
int prod &#61; 1;
int res &#61; 0;

while(r < n)
prod *&#61; nums[r];
while(l <&#61; r && prod >&#61; k)
prod /&#61; nums[l];
l &#43;&#43; ;

// 满足 prod
if(l <&#61; r)
res &#43;&#61; (r - l &#43; 1);
// 加窗口长度

r &#43;&#43; ;

return res;

;

时间复杂度&#xff1a;O(n)
空间复杂度&#xff1a;O(1)


推荐阅读
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • 本文将详细介绍多个流行的 Android 视频处理开源框架,包括 ijkplayer、FFmpeg、Vitamio、ExoPlayer 等。每个框架都有其独特的优势和应用场景,帮助开发者更高效地进行视频处理和播放。 ... [详细]
  • Hadoop发行版本选择指南:技术解析与应用实践
    本文详细介绍了Hadoop的不同发行版本及其特点,帮助读者根据实际需求选择最合适的Hadoop版本。内容涵盖Apache Hadoop、Cloudera CDH等主流版本的特性及应用场景。 ... [详细]
  • 本文详细介绍如何使用CSS自定义HTML5视频播放器的样式,涵盖常见属性及跨浏览器兼容性问题。发布时间:2020-09-14 14:46:29;来源:亿速云;阅读量:58;作者:小新。 ... [详细]
  • 本文详细介绍了如何将 Python 3.6.3 程序转换为 Windows 可执行文件(.exe),并解决了使用 py2exe 和 cx_Freeze 时遇到的问题。推荐使用 PyInstaller 进行打包,提供完整的安装和打包步骤。 ... [详细]
  • 2018-2019学年第六周《Java数据结构与算法》学习总结
    本文总结了2018-2019学年第六周在《Java数据结构与算法》课程中的学习内容,重点介绍了非线性数据结构——树的相关知识及其应用。 ... [详细]
  • 本文详细介绍了优化DB2数据库性能的多种方法,涵盖统计信息更新、缓冲池调整、日志缓冲区配置、应用程序堆大小设置、排序堆参数调整、代理程序管理、锁机制优化、活动应用程序限制、页清除程序配置、I/O服务器数量设定以及编入组提交数调整等方面。通过这些技术手段,可以显著提升数据库的运行效率和响应速度。 ... [详细]
  • 嵌入式开发环境搭建与文件传输指南
    本文详细介绍了如何为嵌入式应用开发搭建必要的软硬件环境,并提供了通过串口和网线两种方式将文件传输到开发板的具体步骤。适合Linux开发初学者参考。 ... [详细]
  • 解决TensorFlow CPU版本安装中的依赖问题
    本文记录了在安装CPU版本的TensorFlow过程中遇到的依赖问题及解决方案,特别是numpy版本不匹配和动态链接库(DLL)错误。通过详细的步骤说明和专业建议,帮助读者顺利安装并使用TensorFlow。 ... [详细]
  • 本文介绍如何利用栈数据结构在C++中判断字符串中的括号是否匹配。通过顺序栈和链栈两种方式实现,并详细解释了算法的核心思想和具体实现步骤。 ... [详细]
  • 使用Nginx反向代理实现多域名端口映射
    本文介绍如何通过配置本地hosts文件和Nginx反向代理,实现多个虚拟域名的端口映射,使用户可以通过标准HTTP端口80访问不同后端服务。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • 探讨在PHP开发中,如何选择使用Cookie或数据库来优化网站性能,特别是在处理用户保存的搜索结果时。 ... [详细]
  • 优化网页加载速度:JavaScript 实现图片延迟加载
    本文介绍如何使用 JavaScript 实现图片延迟加载,从而显著提升网页的加载速度和用户体验。 ... [详细]
author-avatar
荣哥918
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有